#pragma
#include <queue>
using namespace std;

const int DEF_CAP = 5;

template <class T>
class BlockQueue
{
public:
    queue<T> _bq;
    int _capacity;
    pthread_mutex_t _mtx;
    pthread_cond_t _full;
    pthread_cond_t _empty;

    bool IsFull()
    {
        return _bq.size() == _capacity;
    }

    bool IsEmpty()
    {
        return _bq.size() == 0;
    }

public:
    BlockQueue(int capacity = DEF_CAP)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_full, nullptr);
        pthread_cond_init(&_empty, nullptr);
    }
    int  bqsize()
    {
        return _bq.size();
    }
    void Push(const T &in)
    {
        // pthread_mutex_lock(&_mtx);
        // if (IsFull())
        //     pthread_cond_wait(&_full, &_mtx);
        // _bq.push(in);
        // //if (_bq.size() >= _capacity / 2)
        //     pthread_cond_signal(&_empty); // 当生产数量超过容量的一半时，再通知消费者消费
        // pthread_mutex_unlock(&_mtx);
        
        // ？？？？？？
        // 在解锁前后都可以调用cond_signal函数唤醒线程。
        // 可能出现各种各样的情况，如解锁后，锁被其他非条件变量等待的线程获得，尤其进行生产和消费行为。
        // 此时再唤醒线程，线程又会去等待互斥锁。
        // 但不管发生何种情况，我们其实并不关心到底是哪个线程进行消费和生产，我们只关心消费和生产的行为。

        // lockguard
        LockGuard lockguard(&_mtx);
        while (IsFull())
            pthread_cond_wait(&_full, &_mtx);
        _bq.push(in);
        // if (_bq.size() >= _capacity / 2)
        pthread_cond_signal(&_empty); // 当生产数量超过容量的一半时，再通知消费者消费
    }
    void Pop(T *out)
    {
        // pthread_mutex_lock(&_mtx);
        // if (IsEmpty())
        //     pthread_cond_wait(&_empty, &_mtx);
        // *out = _bq.front();
        // _bq.pop();
        // pthread_cond_signal(&_full);
        // pthread_mutex_unlock(&_mtx);
        

        lockguard
        LockGuard lockguard(&_mtx);
        while (IsEmpty())
            pthread_cond_wait(&_empty, &_mtx);
        *out = _bq.front();
        _bq.pop();
        pthread_cond_signal(&_full);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
    }
};